#ifndef G3_SCENEGRAPH_H
#define G3_SCENEGRAPH_H

#include "math/m_matrix.h"
#include "controller.h"
#include <vector>
#include <algorithm>
#include <boost/shared_ptr.hpp>

/*
Yann L

Rules of thumb to get an efficient scenegraph:

A) Don't use an SG for visibility determination. That's not its job. A good SG is not necessarilly a good spatial tree.
B) Don't use an SG for collision queries. Same reasoning as above.
C) Don't use a fixed partitioning scheme (eg. octree) for an SG. Use variable node trees.
D) Don't build your SG on spatial relationships. Build it on logical relationships.

The most important point being, that an SG is abstract structure to represent relationships between objects
(often hierarchical attachements, such as in character animation). It is not a structure to do visibility culling,
nor will it do collision queries. An SG is a highly abstract logical access structure to your scene data. It is used to
efficiently connect gameplay, physics, animation and AI systems to the graphics engine.
*/

namespace G3 {

using std::vector;
using std::find;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;

// TODO:: Implement these objects
// TODO:: Loose octrees && Loose ABT-s
// TODO:: Loose & Tight Octree
enum OBJECT_NODE_TYPE {
	OBJ_NODE_DUMMY,
	OBJ_ANIM_NODE,
	OBJ_NODE_MESH,
	OBJ_NODE_PHYS_MESH,
	OBJ_NODE_SOUND,
	OBJ_NODE_LIGHT,
	OBJ_NODE_MIRROR,
	OBJ_NODE_BSP,
	OBJ_NODE_OCTREE,
	OBJ_NODE_KD,
	OBJ_NODE_QUADTREE
};

class OBJECT_NODE {
	public:
        OBJECT_NODE (OBJECT_NODE_TYPE nodeType = OBJ_NODE_MESH);
		OBJECT_NODE (shared_ptr<OBJECT_NODE> nodeParent, OBJECT_NODE_TYPE nodeType = OBJ_NODE_MESH);
		virtual ~OBJECT_NODE ();

		bool visible;

		vector<shared_ptr<OBJECT_NODE> > children;
		shared_ptr<OBJECT_NODE> parent;

//==============================================================================
// Virtual functions to be overridden
//
//		float ElapsedTime			Time elapsed since the last call
//==============================================================================
		virtual void Render (float elapsedTime = 1.0) = 0;
		virtual void Update (float elapsedTime = 1.0) = 0;

//==============================================================================
// Attaches Node as a child
//==============================================================================
		void Attach (shared_ptr<OBJECT_NODE> node);

//==============================================================================
// Detaches Node from the children list
//
//	Returns a pointer to the detached Node when successful
//			NULL in case of an error
//==============================================================================
		shared_ptr<OBJECT_NODE> Detach (shared_ptr<OBJECT_NODE> node);

//==============================================================================
// Removes Node from the children list
//==============================================================================
		void Remove (shared_ptr<OBJECT_NODE> node);

//==============================================================================
// Destroys this node
//
//		bool DestroyChildren		Whether or not to also destroy children nodes
//				true				(Default), destroyes this node along with its children
//				false				Uploads children before destroying this node
//==============================================================================
		void Destroy (bool destroyChildren = true);

//==============================================================================
// Attaches all children to the Parent node
//
//	NOTE:: Also clears the current node's children list
//==============================================================================
		void UploadChildren ();

//==============================================================================
// Attaches parent as a child
//
//	NOTE:: At first it detaches the parent from the tree and then attaches it
//		   as a child node
//==============================================================================
		void DownloadParent ();

//==============================================================================
// Finds a child
//
//	Returns Children.end () when the child was not found
//			Vector iterator when the child was found
//==============================================================================
		vector<shared_ptr<OBJECT_NODE> >::iterator FindChild (shared_ptr<OBJECT_NODE> child);

//==============================================================================
// Gets the type of this node
//==============================================================================
		OBJECT_NODE_TYPE GetNodeType ();

	// We should not be able to change node type
	protected:
		OBJECT_NODE_TYPE type;
};

class DUMMY_NODE: public OBJECT_NODE {
	public:
        DUMMY_NODE ();
		DUMMY_NODE (shared_ptr<OBJECT_NODE> nodeParent);
		virtual ~DUMMY_NODE ();

		void Render (float elapsedTime = 1.0);
		void Update (float elapsedTime = 1.0);
};

class ANIM_NODE: public OBJECT_NODE {
    public:
        ANIM_NODE ();
		ANIM_NODE (shared_ptr<OBJECT_NODE> nodeParent, OBJECT_NODE_TYPE nodeType = OBJ_NODE_MESH);
		virtual ~ANIM_NODE ();

   		vector<CONTROLLER *> controllers;

		G3::MATRIX4x4F model_matrix;
		G3::MATRIX4x4F world_matrix;

//==============================================================================
// Virtual functions to be overridden
//
//		float ElapsedTime			Time elapsed since the last call
//==============================================================================
		void Render (float elapsedTime = 1.0);
		void Update (float elapsedTime = 1.0);

//==============================================================================
// Updates children' WorldMatrices
//==============================================================================
		void PropagateMatrix ();

//==============================================================================
// Attaches or detaches a controller
//==============================================================================
		void AttachController (CONTROLLER *ctrl);
		void DetachController (CONTROLLER *ctrl);
};

class SCENEGRAPH: public SINGLETON<SCENEGRAPH> {
	public:
        SCENEGRAPH () {}
        ~SCENEGRAPH () {}

		DUMMY_NODE root;

		void Render (float elapsedTime = 1.0);
		void Update (float elapsedTime = 1.0);
};

extern SCENEGRAPH Scenegraph;

}

#endif
